package org.kernely.stream.userpage;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;

import org.kernely.security.dao.UserDetailsDAO;
import org.kernely.security.persistence.Role;
import org.kernely.security.persistence.User;
import org.kernely.security.persistence.UserDetails;
import org.kernely.stream.StreamBundle;
import org.kernely.stream.dao.MessageDAO;
import org.kernely.stream.dao.StreamDAO;
import org.kernely.stream.persistence.Message;
import org.kernely.stream.persistence.Stream;
import org.kernely.stream.utils.MessageComparator;
import org.kernely.stream.utils.MessageComponent;
import org.kernely.ui.mvp.KernelyEvent;
import org.kernely.ui.mvp.KernelyEventReceiver;
import org.kernely.ui.mvp.KernelyPresenter;

import com.vaadin.event.FieldEvents.TextChangeEvent;
import com.vaadin.event.FieldEvents.TextChangeListener;
import com.vaadin.event.LayoutEvents.LayoutClickEvent;
import com.vaadin.event.LayoutEvents.LayoutClickListener;
import com.vaadin.terminal.ExternalResource;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Label;
import com.vaadin.ui.Layout;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;

@SuppressWarnings("serial")
public class StreamMessagesPresenter extends KernelyPresenter<StreamMessagesView> implements KernelyEventReceiver {

	private Integer oldestDisplayedMessage = -1;
	private Integer newestLoadedMessage = -1;
	private int nbMessagesDisplayed = 10;
	private Thread t = new Thread(new OldMessageLoaderRunnable());
	private Thread poolThread = new Thread(new NewMessageLoaderRunnable());

	@Override
	/**
	 * Fill the UserPageView with data.
	 */
	public void bind() {

		// Registering as an event receiver to be called by StreamUserPageAttachedEvent
		StreamBundle.getEventBus().addListener(this);

		this.view.getStreamSettingsButton().addListener(new ClickListener(){
			/**
			 * Action called when the button is clicked.
			 */
			public void buttonClick (Button.ClickEvent event) {
				displayStreamSettingsWindow();
			}
		});

		poolThread.start();

		this.view.getLengthLabel().setValue("Remain : " + Message.MESSAGE_LENGTH);

		this.view.getInputMessage().addListener(new TextChangeListener() {
			public void textChange(TextChangeEvent event) {
				int inputLength = Message.MESSAGE_LENGTH - event.getText().length();
				view.getLengthLabel().setValue("Remain : " + inputLength);
				if(inputLength < 0 || inputLength >= Message.MESSAGE_LENGTH){
					view.getPostButton().setEnabled(false);
					if(inputLength <= 0){
						view.getInfoLabel().setValue("<span style=\"color:red;\">Your message is too long.</span>");
					}
					if(inputLength >= Message.MESSAGE_LENGTH){
						view.getInfoLabel().setValue("<span style=\"color:red;\">You have to enter a message.</span>");
					}
				}
				else{
					if(!view.getPostButton().isEnabled() && inputLength >= 0 && inputLength < Message.MESSAGE_LENGTH){
						view.getPostButton().setEnabled(true);
						view.getInfoLabel().setValue("");
					}
				}

			}
		});
		
		this.view.getLengthCommentLabel().setValue("Remain : " + Message.MESSAGE_LENGTH);

		this.view.getInputComment().addListener(new TextChangeListener() {
			public void textChange(TextChangeEvent event) {
				int inputLength = Message.MESSAGE_LENGTH - event.getText().length();
				view.getLengthCommentLabel().setValue("Remain : " + inputLength);
				if(inputLength < 0 || inputLength >= Message.MESSAGE_LENGTH){
					view.getPostCommentButton().setEnabled(false);
					if(inputLength <= 0){
						view.getInfoCommentLabel().setValue("<span style=\"color:red;\">Your message is too long.</span>");
					}
					if(inputLength >= Message.MESSAGE_LENGTH){
						view.getInfoCommentLabel().setValue("<span style=\"color:red;\">You have to enter a message.</span>");
					}
				}
				else{
					if(!view.getPostCommentButton().isEnabled() && inputLength >= 0 && inputLength < Message.MESSAGE_LENGTH){
						view.getPostCommentButton().setEnabled(true);
						view.getInfoCommentLabel().setValue("");
					}
				}

			}
		});

		this.view.getPostButton().addListener(new ClickListener(){
			// Called when the button is clicked
			public void buttonClick (Button.ClickEvent event) {
				postMessage();
			}
		});
		
		this.view.getPostCommentButton().addListener(new ClickListener(){
			// Called when the button is clicked
			public void buttonClick (Button.ClickEvent event) {
				postComment();
			}
		});

		this.view.getMoreResults().addListener(new ClickListener() {
			public void buttonClick(ClickEvent event) {
				if(!t.isAlive()){
					t.run();
				}
			}
		});

		// Fill the streams combo box with all the available streams
		// (i.e. streams where the user can post (write permission)
		for (Stream stream : StreamDAO.getInstance().getAllStream()){
			//A user can only write on some streams (or all if he is administrator)
			boolean isAdmin = StreamBundle.getSecurityService().currentUserHasOneOf(Role.ROLE_ADMINISTRATOR);
			boolean hasRights = StreamBundle.getSecurityService().currentUserHasPermission(stream.getCategory()+":write:"+stream.getUid());
			if (isAdmin || hasRights){
				//Add the stream
				this.view.getStreamsBox().addItem(stream);
				this.view.getStreamsBox().setItemCaption(stream, stream.getTitle());
			}
		}
		String currentUserLogin = StreamBundle.getSecurityService().getCurrentUser().getLogin();
		Stream userStream = StreamDAO.getInstance().findStream(Stream.CATEGORY_USERS, currentUserLogin);

		// Set the stream to the user's stream
		this.view.getStreamsBox().setValue(userStream);

	}
	
	private void postMessage(){
		String content = this.view.getInputMessage().getValue().toString();
		if (content != null && content != "" ){
			Message newMessage = new Message();
			newMessage.setContent(content);
			// Write on user's stream
			newMessage.setUser(StreamBundle.getSecurityService().getCurrentUser());
			java.sql.Date date = new java.sql.Date(new Date().getTime());
			newMessage.setDate(date);

			Stream stream = (Stream) this.view.getStreamsBox().getValue();
			newMessage.setStream(stream);
			MessageDAO.getInstance().saveMessage(newMessage);
			this.view.getInputMessage().setValue("");
			this.view.getInputMessage().focus();
			Layout layout = loadNewMessages(true);
			if(layout != null){
				view.getNewMessagesLayout().addComponent(layout, 0);
			}
			this.view.getLengthLabel().setValue("Remain : " + Message.MESSAGE_LENGTH);
		}
	}
	
	private void postComment(){
		String content = this.view.getInputComment().getValue().toString();
		if (content != null && content != "" ){
			Message newComment = new Message();
			newComment.setContent(content);
			// Write on user's stream
			newComment.setUser(StreamBundle.getSecurityService().getCurrentUser());
			java.sql.Date date = new java.sql.Date(new Date().getTime());
			newComment.setDate(date);

			int messageId = Integer.valueOf(view.getMessageDisplayed().toString());
			Message message = MessageDAO.getInstance().findMessageById(messageId);
			newComment.setMessage(message);
			MessageDAO.getInstance().saveMessage(newComment);
			this.view.getInputComment().setValue("");
			this.view.getInputComment().focus();
			loadComments(message);
			this.view.getLengthCommentLabel().setValue("Remain : " + Message.MESSAGE_LENGTH);
		}
	}


	private void displayStreamSettingsWindow(){
		StreamSettingsWindowPresenter presenter  = new StreamSettingsWindowPresenter();
		StreamSettingsWindowView view = new StreamSettingsWindowView();
		presenter.setView(view);

		Window subwindow = new Window("Kernely - Edit stream settings");
		subwindow.setModal(true);
		subwindow.setResizable(false);
		this.view.getWindow().addWindow(subwindow);
		subwindow.getContent().setSizeUndefined();

		subwindow.setContent((Layout)presenter.getView());
		subwindow.setIcon(new ExternalResource("/streambundle/images/custom.png"));
	}


	/**
	 * Display messages, only when the view has been attached, to get Application reference.
	 */
	public void handleKernelyEvent(KernelyEvent event) {
		if (event instanceof StreamMustDisplayMessagesEvent){
			oldestDisplayedMessage = -1; // To force the removal and then the display of all messages.
			if(!t.isAlive()){
				t.run();
			}
		}
	}	

	/**
	 * Actualize the displayed messages (add new messages, don't reload all messages).
	 */
	private synchronized Layout listMessages(){
		TreeSet<Message> ordered = new TreeSet<Message>(new MessageComparator());
		MessageDAO mdao = MessageDAO.getInstance();
		User current = StreamBundle.getSecurityService().getCurrentUser();
		VerticalLayout packLayout = new VerticalLayout();

		if (oldestDisplayedMessage == -1){
			// Remove all the streams.
			view.getAllMessageLayout().removeAllComponents();
			view.getNewMessagesLayout().removeAllComponents();
			Set<Message> set = mdao.getUserMessages(current, 0, nbMessagesDisplayed);
			ordered.addAll(set);
			if(!ordered.isEmpty()){
				newestLoadedMessage = ordered.last().getId();
			}
		}
		else{
			ordered.addAll(mdao.getNextMessages(current, mdao.findMessageById(oldestDisplayedMessage), nbMessagesDisplayed));
		}
		if(ordered.isEmpty()){
			return null;
		}
		oldestDisplayedMessage = ordered.first().getId();

		// Display all messages
		for (final Message message : ordered){
			MessageComponent mc = new MessageComponent(message,view.getApplication());
			mc.addListener(new LayoutClickListener() {
				public void layoutClick(LayoutClickEvent event) {
					displayMessageDetails(message);
				}
			});
			packLayout.addComponent(mc,0);
		}
		return packLayout;
	}

	public void addNewBlock(Layout layout){
		if(layout != null){
			view.getAllMessageLayout().addComponent(layout);
		}
	}

	public class OldMessageLoaderRunnable implements Runnable {
		public void run() {
			Layout layout = listMessages();
			addNewBlock(layout);
		}
	}

	public class NewMessageLoaderRunnable implements Runnable {
		public void run() {
			for(;;){
				try {
					Thread.sleep(5000);
					Layout layout = loadNewMessages(false);
					if(layout != null){
						view.getNewMessagesLayout().addComponent(layout, 0);
					}
				} catch (InterruptedException e) {
					// TODO CHANGE STACKTRACE
					e.printStackTrace();
				}
			}
		}
	}

	public synchronized Layout loadNewMessages(boolean fromPostMessage){
		if(newestLoadedMessage != -1 || fromPostMessage){
			TreeSet<Message> ordered = new TreeSet<Message>(new MessageComparator());
			MessageDAO mdao = MessageDAO.getInstance();
			User current = StreamBundle.getSecurityService().getCurrentUser();
			VerticalLayout packLayout = new VerticalLayout();

			Set<Message> set = mdao.getAllRecentMessage(current, mdao.findMessageById(newestLoadedMessage));
			ordered.addAll(set);
			if(ordered.isEmpty()){
				return null;
			}
			newestLoadedMessage = ordered.last().getId();
			// Display all messages
			for (final Message message : ordered){
				MessageComponent mc = new MessageComponent(message,view.getApplication());
				mc.addListener(new LayoutClickListener() {
					public void layoutClick(LayoutClickEvent event) {
						displayMessageDetails(message);
					}
				});
				packLayout.addComponent(mc ,0);
			}
			return packLayout;
		}
		else{
			view.getNewMessagesLayout().removeAllComponents();
			return null;
		}
	}

	public void displayMessageDetails(Message message){
		// Writer infos
		view.getMessageDisplayed().setValue(message.getId());
		view.getPostCommentPanel().setEnabled(true);
		UserDetails details = UserDetailsDAO.getInstance().getUserDetails(message.getUser());

		//Date
		SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
		SimpleDateFormat dateHourFormat = new SimpleDateFormat("kk:mm:ss");
		view.getStreamName().setValue(message.getStream().getTitle());
		view.getMessageContent().setValue(
				"<br/><span style=\"font-style:italic;color:grey\">From "+details.getFirstname()+" "+details.getName()+", the "+dateFormat.format(message.getDate())+" at "+dateHourFormat.format(message.getDate())+" :</span>"
				+ "<br/>" +  message.getContent());
		loadComments(message);
	}
	
	public void loadComments(Message message){
		view.getCommentLayout().removeAllComponents();
		SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
		SimpleDateFormat dateHourFormat = new SimpleDateFormat("kk:mm:ss");
		TreeSet<Message> ordered = new TreeSet<Message>(new MessageComparator());
		ordered.addAll(MessageDAO.getInstance().getAllCommentsFromMessage(message));
		for(Message c : ordered){
			UserDetails detailsComment = UserDetailsDAO.getInstance().getUserDetails(c.getUser());
			Label comment = new Label(c.getContent() +
					"<br/><span style=\"font-style:italic;color:grey\">From "+detailsComment.getFirstname()+" "+detailsComment.getName()+", the "+dateFormat.format(c.getDate())+" at "+dateHourFormat.format(c.getDate())+".</span>" +"<hr/>", Label.CONTENT_XHTML);
			comment.setSizeUndefined();
			view.getCommentLayout().addComponent(comment,0);
			view.getCommentLayout().setComponentAlignment(comment, Alignment.MIDDLE_RIGHT);
		}
	}

}